package com.clei.utils;

import com.clei.dto.ExcelVO;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * csv工具类
 *
 * @author Y
 * @date 2022-05-27
 */
public final class CsvUtil {

    /**
     * 带bom的utf8 bytes
     */
    private static final byte[] UTF_BOM = new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};

    /**
     * 分隔符
     */
    private static final String SEPARATOR = "\t,";

    /**
     * 要被替换的字符串
     */
    private static final String OLD_STR_REGEX = "[\r\n,]";

    /**
     * 分隔符替换符
     */
    private static final String REPLACEMENT = "，";

    /**
     * obj为null时的默认字符串
     */
    private static final String NULL_STR = "--";

    /**
     * csv导出
     * 有自定义列的
     *
     * @param dataList   数据list
     * @param clazz      数据类型class
     * @param columnList 自定义列
     * @param filePath   输出文件路径
     */
    public static void export(List<?> dataList, Class<?> clazz, List<Map<String, String>> columnList, String filePath) {
        // 标题和key
        List<String> titleList = new ArrayList<>(columnList.size());
        List<String> keyList = new ArrayList<>(columnList.size());
        for (Map<String, String> c : columnList) {
            titleList.add(c.get("name"));
            keyList.add(StringUtil.snake2Camel(c.get("key")));
        }
        Map<String, Field> fieldMap = Arrays.stream(clazz.getDeclaredFields())
                .collect(Collectors.toMap(Field::getName, f -> f));
        // 保证key与title的顺序一致
        List<Field> fieldList = new ArrayList<>(keyList.size());
        for (String k : keyList) {
            Field f = fieldMap.get(k);
            if (null != f) {
                f.setAccessible(true);
                fieldList.add(f);
            }
        }
        // lineProducer
        int fieldSize = fieldList.size();
        Function<Object, String> lineProducer = obj -> {
            try {
                List<Object> objList = new ArrayList<>(fieldSize);
                for (Field field : fieldList) {
                    objList.add(field.get(obj));
                }
                return objList2String(objList);
            } catch (Exception e) {
                PrintUtil.log("获取导出字符串出错", e);
                return null;
            }
        };
        List<Object> objList = (List<Object>) dataList;
        // 导出
        export(titleList, objList, lineProducer, filePath);
    }

    private CsvUtil() {
    }

    /**
     * csv导出
     *
     * @param titleList 标题list
     * @param dataList  数据list
     * @param filePath  输出文件路径
     */
    public static void export(List<String> titleList, List<ExcelVO> dataList, String filePath) {
        Function<ExcelVO, String> lineProducer = vo -> objList2String(vo.getValues());
        // 导出
        export(titleList, dataList, lineProducer, filePath);
    }

    /**
     * csv导出
     *
     * @param titleList    标题list
     * @param dataList     数据list
     * @param lineProducer 行数据生产者
     * @param filePath     导出文件路径
     * @param <T>          泛型
     */
    public static <T> void export(List<String> titleList, List<T> dataList, Function<T, String> lineProducer, String filePath) {
        File file = new File(filePath);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        try (FileOutputStream fos = new FileOutputStream(file);
             OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
             BufferedWriter bw = new BufferedWriter(osw)) {
            // BOM
            fos.write(UTF_BOM);
            // 标题
            String title = String.join(SEPARATOR, titleList);
            bw.write(title);
            bw.newLine();
            // 内容
            for (T t : dataList) {
                String str = lineProducer.apply(t);
                if (null != str) {
                    bw.write(str);
                    bw.newLine();
                }
            }
            bw.flush();
        } catch (Exception e) {
            PrintUtil.log("导出csv出错 ", e);
        }
    }

    /**
     * csv导出
     *
     * @param titleList 标题list
     * @param dataList  数据list
     * @param filePath  输出文件路径
     */
    public static void exportByObj(List<String> titleList, List<List<Object>> dataList, String filePath) {
        Function<List<Object>, String> lineProducer = CsvUtil::objList2String;
        // 导出
        export(titleList, dataList, lineProducer, filePath);
    }

    /**
     * csv文件合并
     *
     * @param sourceFilePath 源文件路径
     * @param outputFilePath 输出文件路径
     */
    public static void merge(String sourceFilePath, String outputFilePath) {
        File inputFile = new File(sourceFilePath);
        File[] files = inputFile.listFiles();
        if (null == files) {
            return;
        }
        // 排序 避免合并后顺序错乱
        Arrays.sort(files, Comparator.comparingInt(f -> Integer.parseInt(f.getName().replace(".csv", ""))));
        try (FileOutputStream fos = new FileOutputStream(outputFilePath);
             OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
             BufferedWriter bw = new BufferedWriter(osw)) {

            int i = 0;
            // 合并
            for (File file : files) {
                try (FileReader rw = new FileReader(file);
                     BufferedReader br = new BufferedReader(rw)) {
                    // 获取第一个文件的标题
                    String str = br.readLine();
                    if (i == 0 && null != str) {
                        bw.write(str);
                        bw.newLine();
                    }
                    while (null != (str = br.readLine())) {
                        bw.write(str);
                        bw.newLine();
                    }
                }
                file.delete();
                i++;
            }
            bw.flush();
        } catch (Exception e) {
            PrintUtil.log("csv文件合并出错", e);
        }
    }

    /**
     * objList -> String
     *
     * @param objList objList
     * @return String
     */
    private static String objList2String(List<Object> objList) {
        // 先判断是不是null，再判断是不是String，再判断是不是Date
        return objList.stream().map(o -> null == o
                        ? NULL_STR : o instanceof Date
                        ? DateUtil.format((Date) o) : replace(o.toString()))
                .collect(Collectors.joining(SEPARATOR));
    }

    /**
     * 影响excel查看csv格式的特殊符号替换
     *
     * @param str String
     * @return String
     */
    private static String replace(String str) {
        str = str.replaceAll(OLD_STR_REGEX, REPLACEMENT);
        // 双引号替换为单引号
        return str.replaceAll("\"", "'");
    }
}
